// 版权所有2010 Go作者。保留所有权利。
// 此源代码的使用受BSD样式
// 许可证的约束，该许可证可以在许可证文件中找到。

package image

import (
	"bufio"
	"errors"
	"io"
	"sync"
	"sync/atomic"
)

// ErrFormat表示解码遇到未知格式。
var ErrFormat = errors.New("image: unknown format")

// 格式包含图像格式的名称、魔法头以及如何解码。
type format struct {
	name, magic  string
	decode       func(io.Reader) (Image, error)
	decodeConfig func(io.Reader) (Config, error)
}

// 格式是注册格式的列表。
var (
	formatsMu     sync.Mutex
	atomicFormats atomic.Value
)

// RegisterPerformat注册一种图像格式以供解码使用。
// Name是格式的名称，如“jpeg”或“png”。
// Magic是标识格式编码的Magic前缀。神奇的
// 字符串可以包含“？”每个匹配任意一个字节的通配符。
// 解码是对编码图像进行解码的函数。
// DecodeConfig是只对其配置进行解码的函数。
func RegisterFormat(name, magic string, decode func(io.Reader) (Image, error), decodeConfig func(io.Reader) (Config, error)) {
	formatsMu.Lock()
	formats, _ := atomicFormats.Load().([]format)
	atomicFormats.Store(append(formats, format{name, magic, decode, decodeConfig}))
	formatsMu.Unlock()
}

// 读卡器就是io。读者也可以向前看。
type reader interface {
	io.Reader
	Peek(int) ([]byte, error)
}

// asReader转换io。读者对读者。
func asReader(r io.Reader) reader {
	if rr, ok := r.(reader); ok {
		return rr
	}
	return bufio.NewReader(r)
}

// 匹配报告魔法是否与b匹配。魔法可能包含“？”通配符。
func match(magic string, b []byte) bool {
	if len(magic) != len(b) {
		return false
	}
	for i, c := range b {
		if magic[i] != c && magic[i] != '?' {
			return false
		}
	}
	return true
}

// Sniff确定r数据的格式。
func sniff(r reader) format {
	formats, _ := atomicFormats.Load().([]format)
	for _, f := range formats {
		b, err := r.Peek(len(f.magic))
		if err == nil && match(f.magic, b) {
			return f
		}
	}
	return format{}
}

// 解码以注册格式编码的图像。
// 返回的字符串是格式注册期间使用的格式名称。
// 格式注册通常由编解码器中的init函数完成-
// 特定包。
func Decode(r io.Reader) (Image, string, error) {
	rr := asReader(r)
	f := sniff(rr)
	if f.decode == nil {
		return nil, "", ErrFormat
	}
	m, err := f.decode(rr)
	return m, f.name, err
}

// DecodeConfig对已注册格式编码的图像的颜色模型和尺寸进行解码。返回的字符串是格式注册过程中使用的格式名
// 。格式注册通常由
// 编解码器特定包中的初始化函数完成。
func DecodeConfig(r io.Reader) (Config, string, error) {
	rr := asReader(r)
	f := sniff(rr)
	if f.decodeConfig == nil {
		return Config{}, "", ErrFormat
	}
	c, err := f.decodeConfig(rr)
	return c, f.name, err
}
